/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.inspur.edp.metadata.rtcustomization.servermanager;

import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.entity.MetadataDto;
import com.inspur.edp.lcm.metadata.common.MetadataDtoConverter;
import com.inspur.edp.lcm.metadata.common.MetadataSerializer;
import com.inspur.edp.lcm.metadata.serverapi.NoCodeServerService;
import com.inspur.edp.lcm.metadata.spi.event.nodecode.NoCodeAfterDeleteArgs;
import com.inspur.edp.lcm.metadata.spi.event.nodecode.NoCodeAfterSaveArgs;
import com.inspur.edp.lcm.metadata.spi.event.nodecode.NoCodeBeforeDeleteArgs;
import com.inspur.edp.lcm.metadata.spi.event.nodecode.NoCodeBeforeSaveArgs;
import com.inspur.edp.lcm.metadata.spi.event.nodecode.NoCodeEventArgs;
import com.inspur.edp.metadata.rtcustomization.api.entity.DtSourceTypeEnum;
import com.inspur.edp.metadata.rtcustomization.api.entity.GspMdContent;
import com.inspur.edp.metadata.rtcustomization.api.entity.GspMdRefs;
import com.inspur.edp.metadata.rtcustomization.api.entity.SourceTypeEnum;
import com.inspur.edp.metadata.rtcustomization.api.exception.ErrorCodes;
import com.inspur.edp.metadata.rtcustomization.api.exception.LcmEventException;
import com.inspur.edp.metadata.rtcustomization.api.exception.LcmMetadataException;
import com.inspur.edp.metadata.rtcustomization.serverapi.CustomizationServerService;
import com.inspur.edp.metadata.rtcustomization.servermanager.dac.CustomizationDataProducer;
import com.inspur.edp.metadata.rtcustomization.servermanager.dac.CustomizationMetadataRefsRepository;
import com.inspur.edp.metadata.rtcustomization.servermanager.dac.MetadataContentHisRepository;
import com.inspur.edp.metadata.rtcustomization.servermanager.dac.MetadataContentRepository;
import com.inspur.edp.metadata.rtcustomization.servermanager.dac.MetadataRtContentRepository;
import com.inspur.edp.metadata.rtcustomization.servermanager.event.NoCodeServiceEventBroker;
import com.inspur.edp.metadata.rtcustomization.servermanager.repository.CustomizationRepoService;
import com.inspur.edp.metadata.rtcustomization.servermanager.repository.MetadataRefsRepoService;
import io.iec.edp.caf.commons.transaction.JpaTransaction;
import io.iec.edp.caf.commons.transaction.TransactionPropagation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;

/**
 * 功能描述:
 *
 * @ClassName: NoCodeServerServiceImpl
 * @Author: Fynn Qi
 * @Date: 2021/5/25 16:02
 * @Version: V1.0
 */
@Slf4j
public class NoCodeServerServiceImpl implements NoCodeServerService {

    private final MetadataSerializer serializer;
    private final CustomizationServerService serverService;
    private final CustomizationServerServiceImpl serverServiceImpl;
    private final NoCodeServiceEventBroker eventBroker;
    private final CustomizationRepoService repoService;
    private final MetadataContentRepository mdContentRepo;
    private final MetadataRtContentRepository mdRtContentRepo;

    private final CustomizationMetadataRefsRepository metadataRefsRepository;
    private final MetadataRefsRepoService metadataRefsRepoService;

    public NoCodeServerServiceImpl(
            CustomizationServerService serverService,
            NoCodeServiceEventBroker broker,
            MetadataContentRepository mdContentRepo,
            MetadataRtContentRepository mdRtContentRepo,
            CustomizationMetadataRefsRepository metadataRefsRepository,
            MetadataContentHisRepository mdContentHisRepo,
            CustomizationDataProducer cdp) {
        this.serverService = serverService;
        this.serverServiceImpl = (CustomizationServerServiceImpl) serverService;
        this.eventBroker = broker;
        this.metadataRefsRepoService = new MetadataRefsRepoService(metadataRefsRepository);
        this.repoService = new CustomizationRepoService(mdContentRepo, mdContentHisRepo, cdp, metadataRefsRepository, metadataRefsRepoService);
        this.mdContentRepo = mdContentRepo;
        this.mdRtContentRepo = mdRtContentRepo;
        this.metadataRefsRepository = metadataRefsRepository;
        this.serializer = new MetadataSerializer();
    }

    @Override
    public MetadataDto saveMetadata(MetadataDto metadataDto) {

        GspMetadata metadata = MetadataDtoConverter.asMetadata(metadataDto);

        //1. 元数据校验
        metadataSaveCheck(metadata);

        //2. 组装元数据依赖关系节点
        buildMetadataRef(metadata);

        //3. 保存前事件
        this.eventBroker.fireMetadataBeforeSaveEvent(buildEventArgs(metadata, NoCodeEventArgType.BEFORE_SAVE_EVENT_ARG));

        //4. 执行保存
        save(metadata);

        //5. 保存后事件
        this.eventBroker.fireMetadataAfterSaveEvent(buildEventArgs(metadata, NoCodeEventArgType.AFTER_SAVE_EVENT_ARG));

        //6. 清理缓存
        removeCache(metadata.getHeader().getId());

        return MetadataDtoConverter.asDto(metadata);

    }

    private NoCodeEventArgs buildEventArgs(GspMetadata metadata, NoCodeEventArgType argType) {
        switch (argType) {
            case BEFORE_SAVE_EVENT_ARG:
                return new NoCodeBeforeSaveArgs(metadata);
            case AFTER_SAVE_EVENT_ARG:
                return new NoCodeAfterSaveArgs(metadata);
            case BEFORE_DELETE_EVENT_ARG:
                return new NoCodeBeforeDeleteArgs(metadata);
            case AFTER_DELETE_EVENT_ARG:
                return new NoCodeAfterDeleteArgs(metadata);
            default:
                throw new LcmEventException(ErrorCodes.ECP_EVENT_0002);
        }
    }

    @Transactional
    public void save(GspMetadata metadata) {
        //更新元数据版本
        serverServiceImpl.updateMetadaVersion(metadata);

        //构造GspMdContent，需要设置sourceType
        GspMdContent mdContent = repoService.buildGspMdContentFromGspMetdata(metadata, metadata.getHeader().getId(), false);
        // TODO 扩展中的元数据是否需要保存依赖关系，和运行时元数据的扩展关系是否冲突？
        List<GspMdRefs> metadataRefs = metadataRefsRepoService.buildGspMdRefsByMetadata(metadata);

        // 保存前设置设置sourceType
        mdContent.setSourceType(DtSourceTypeEnum.NOCODE.getCode());
        mdContentRepo.save(mdContent);
        metadataRefsRepoService.saveAll(metadataRefs);
    }

    private void removeCache(String metadataId) {
        serverServiceImpl.RemoveDistCache(metadataId);
    }

    private void buildMetadataRef(GspMetadata metadata) {
        serverServiceImpl.buildMetadataRef(metadata);
    }

    private void metadataSaveCheck(GspMetadata metadata) {
        if (metadata == null || metadata.getHeader() == null || metadata.getContent() == null) {
            throw new LcmMetadataException(ErrorCodes.ECP_METADATA_0003, "metadata");
        }
        serverServiceImpl.metadataSaveCheck4CustomizingAndNocode(metadata);
    }

    @Override
    public MetadataDto getMetadata(String metadataId) {
        GspMetadata metadata = serverService.getMetadata(metadataId);
        return MetadataDtoConverter.asDto(metadata);
    }

    @Override
    public boolean publishMetadata(String metadataId) {
        return serverService.releaseMetadataToRtWithSourceType(metadataId, null, SourceTypeEnum.NOCODE.name());
    }

    @Override
    public void deleteMetadata(String metadataId) {

        //删除前校验
        serverServiceImpl.deleteCheck(metadataId, null);

        GspMetadata metadata = serverServiceImpl.getMetadataByIdAndCertId(metadataId, null);
        if (Objects.isNull(metadata)) {
            return;
        }

        //删除前事件
        eventBroker.fireMetadataBeforeDeleteEvent(buildEventArgs(metadata, NoCodeEventArgType.BEFORE_DELETE_EVENT_ARG));

        //删除元数据
        deleteMetadataById(metadataId);

        //删除后事件
        eventBroker.fireMetadataAfterDeleteEvent(buildEventArgs(metadata, NoCodeEventArgType.AFTER_DELETE_EVENT_ARG));

        //清空缓存
        removeCache(metadata.getHeader().getId());
    }

    private void deleteMetadataById(String metadataId) {
        JpaTransaction tran = JpaTransaction.getTransaction();
        try {
            //此处支持更多选项
            tran.begin(TransactionPropagation.REQUIRED);
            //删除mdcontent
            repoService.deleteCustomizedData(metadataId, null);
            //删除mdrtcontent
            mdRtContentRepo.deleteByMetadataId(metadataId);
            //删除引用关系
            metadataRefsRepository.deleteByMdId(metadataId);
            tran.commit();
        }
        catch (Throwable e){
            try {
                tran.rollback();
            } catch (Exception ex) {
                log.error("deleteMetadataById rollback error", ex);
            }
            throw e;
        }
    }

    /**
     * 参数类型
     */
    private enum NoCodeEventArgType {
        BEFORE_SAVE_EVENT_ARG,
        AFTER_SAVE_EVENT_ARG,
        BEFORE_DELETE_EVENT_ARG,
        AFTER_DELETE_EVENT_ARG
    }
}
